package com.binguo.api.service;


import com.binguo.common.enums.RefundStatus;
import com.binguo.common.exception.ServiceException;
import com.binguo.common.paymodel.*;
import com.binguo.common.util.JsonUtil;
import com.binguo.youtu.sign.MD5;
import com.github.wxpay.sdk.WXPay;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.binguo.common.exception.ErrorCode.WX_PAY_ERROR;

@Service
public class WXPayService {

    public WXPayService(){
        try {
            wxPay = new WXPay(new CustomerWXPayConfig());
        } catch (Exception e) {

        }
    }

    private static Logger logger = LoggerFactory.getLogger(WXPayService.class);

    @Autowired
    ApiOrderService apiOrderService;

    private final int MIN_INDEX = 100000;
    private final int MAX_INDEX = Integer.MAX_VALUE - MIN_INDEX;

    private AtomicInteger indexInteger = new AtomicInteger(MIN_INDEX);

    private WXPay wxPay;

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static String success = "SUCCESS";

    private static final String payUrl = "http://binguoai.com/api/wxpay/callback";

    private static final String papPayUrl = "https://api.mch.weixin.qq.com/pay/pappayapply";

    private static final String deleteContractUrl = "https://api.mch.weixin.qq.com/papay/deletecontract";

    private static final String tengxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    private static final String responseSuccess = "<xml>" +
            "<return_code><![CDATA[SUCCESS]]></return_code>" +
            "<return_msg><![CDATA[OK]]></return_msg>" +
            "</xml>";

    private static final String responseFail = "<xml>" +
            "<return_code><![CDATA[FAIL]]></return_code>" +
            "<return_msg><![CDATA[sign error]]></return_msg>" +
            "</xml>";

    /**
     * 免密支付结果通知地址
     */
    private static final String pap_notify_url = "";


    /**
     * 免密支付
     * @param orderNo 订单号
     * @param totalAmount 总金额（分）
     * @param ipAddress ip
     * @param contract_id 委托代扣协议id
     *
     * @return 受理结果 true 成功 false失败
     */
    public Boolean pappay(String orderNo, String totalAmount, String ipAddress, String contract_id) throws Exception{
        String stringB = "appid=" + WeixinConfig.getAppId()
                + "&body=餐费代扣&contract_id=" + contract_id + "&mch_id=" +
                WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                + WeixinConfig.getPapNotifyUrl()
                + "&out_trade_no=" + orderNo +
                "&spbill_create_ip=" + ipAddress + "&total_fee=" + totalAmount
                + "&trade_type=PAP&key=" + WeixinConfig.getKey();

        String sign2 = MD5Util.MD5Upper32(stringB);

        String res = "<xml>" +
                "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                "<body>餐费代扣</body>" +
                "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                "<notify_url>" + WeixinConfig.getPapNotifyUrl() + "</notify_url>" +
                "<out_trade_no>" + orderNo + "</out_trade_no>" +
                "<spbill_create_ip>" + ipAddress + "</spbill_create_ip>" +
                "<total_fee>" + totalAmount + "</total_fee>" +
                "<trade_type>PAP</trade_type>" +
                "<contract_id>" + contract_id + "</contract_id>" +
                "<sign>" + sign2 + "</sign>" +
                "</xml>";


        System.out.println("res=" + res);


        HttpPost httpPost = new HttpPost(papPayUrl);

        StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        CloseableHttpResponse closeableResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
        String wxresp = EntityUtils.toString(closeableResponse.getEntity(), "utf-8");

        System.out.println("resp=" + wxresp);


        Map<String, String> resultMap = parserXml(wxresp);

        if (resultMap.get("return_code").equals("SUCCESS")) {
            if (resultMap.get("result_code").equals("SUCCESS")) {
                return true;
            }
        }
        return false;
    }


    /**
     * 用户免密支付解约
     * @param contract_id 协议号
     * @return
     * @throws Exception
     */
    public Boolean deletecontract(String contract_id) throws Exception{
        String stringB = "appid=" + WeixinConfig.getAppId()
                + "&contract_id=" + contract_id + "&contract_termination_remark=用户解约&mch_id=" +
                WeixinConfig.getMerId() + "&version=1.0"
                + "&key=" + WeixinConfig.getKey();

        String sign2 = MD5Util.MD5Upper32(stringB);

        String res = "<xml>" +
                "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                "<contract_termination_remark>用户解约</contract_termination_remark>" +
                "<version>1.0</version>" +
                "<contract_id>" + contract_id + "</contract_id>" +
                "<sign>" + sign2 + "</sign>" +
                "</xml>";


        System.out.println("res=" + res);


        HttpPost httpPost = new HttpPost(deleteContractUrl);

        StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        CloseableHttpResponse closeableResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
        String wxresp = EntityUtils.toString(closeableResponse.getEntity(), "utf-8");

        System.out.println("resp=" + wxresp);


        Map<String, String> resultMap = parserXml(wxresp);

        if (resultMap.get("return_code").equals("SUCCESS")) {
            if (resultMap.get("result_code").equals("SUCCESS")) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取签约相关参数
     * @return
     */
    public Map<String, String> getContractInfo(){

        //时间戳
        long now = System.currentTimeMillis();
        //自增序列号
        int i = indexInteger.getAndIncrement();

        if(i > MAX_INDEX){
            indexInteger.set(MIN_INDEX);
        }

        //格式化订单号（时间戳+自增序号）
        StringBuilder sb = new StringBuilder();
        sb.append(now).append(i);

        String timestamp = now/1000 + "";

        Map<String, String> infoMap = new HashMap<>();
        infoMap.put("appid", WeixinConfig.getAppId());
        infoMap.put("contract_code", sb.toString());
        infoMap.put("contract_display_account", "云澎");
        infoMap.put("mch_id", WeixinConfig.getMerId());
        infoMap.put("notify_url", WeixinConfig.getContractNotifyUrl());
        infoMap.put("plan_id", WeixinConfig.getPlanId());
        infoMap.put("request_serial", i + "");
        infoMap.put("timestamp", timestamp);

        String stringB = "appid=" + WeixinConfig.getAppId()
                + "&contract_code=" + sb.toString() + "&contract_display_account=云澎&mch_id=" +
                WeixinConfig.getMerId() + "&notify_url=" + WeixinConfig.getContractNotifyUrl()
                + "&plan_id=" + WeixinConfig.getPlanId()
                + "&request_serial=" + i
                + "&version=1.0"
                + "&key=" + WeixinConfig.getKey();

        String sign2 = MD5Util.MD5Upper32(stringB);


        infoMap.put("sign", sign2);

        return infoMap;
    }


    /**
     * 签约/解约通知
     * @param request
     * @return
     */
    public String contractNotify(HttpServletRequest request) {
        logger.info("contractNotify");

        String requestBody = getRequestBody(request);

        Map<String, String> resultMap = parserXml(requestBody);

        logger.info("weixin contractNotify resultMap:  " + resultMap  );

        String stringA = getPostSign(resultMap);

        String stringSignTemp = stringA + "key=" + WeixinConfig.key;

        String sign = MD5Util.MD5Upper32(stringSignTemp);

        if (!sign.equals(resultMap.get("sign")))
        {
            return responseFail;
        }

        if (resultMap.get("return_code").equals("SUCCESS")) {
            if (resultMap.get("result_code").equals("SUCCESS")) {
                String change_type = resultMap.get("change_type");//签约类型: ADD 签约，  DELETE 解约
                String contract_id = resultMap.get("contract_id");//签约协议号--微信生成
                String openid = resultMap.get("openid");//用户openid
                String contract_code = resultMap.get("contract_code");//签约协议号--我方生成

                if ("ADD".equals(change_type)) {
                    //todo 签约
                } else if ("DELETE".equals(change_type)) {
                    //todo 解约
                }

            }
        }
        return responseSuccess;
    }


    /**
     * 支付通知回调
     * @param request
     * @return
     */
	public String notify(HttpServletRequest request) {
        logger.info("notify");

        String requestBody = getRequestBody(request);

        Map<String, String> resultMap = parserXml(requestBody);

        logger.info("weixin pay resultMap:  " + resultMap  );

        String stringA = getPostSign(resultMap);

        String stringSignTemp = stringA + "key=" + WeixinConfig.key;

        String sign = MD5Util.MD5Upper32(stringSignTemp);

        if (!sign.equals(resultMap.get("sign")))
        {
            return responseFail;
        }

        String appid = resultMap.get("appid");
        String mch_id = resultMap.get("mch_id");
        String device_info = resultMap.get("device_info");
        String nonce_str = resultMap.get("nonce_str");
        String result_code = resultMap.get("result_code");
        String err_code = resultMap.get("err_code");
        String err_code_des = resultMap.get("err_code_des");
        String openid = resultMap.get("openid");
        String is_subscribe = resultMap.get("is_subscribe");
        String trade_type = resultMap.get("trade_type");
        String bank_type = resultMap.get("bank_type");
        String total_fee = resultMap.get("total_fee");
        String fee_type = resultMap.get("fee_type");
        String cash_fee = resultMap.get("cash_fee");
        String cash_fee_type = resultMap.get("cash_fee_type");
        String coupon_fee = resultMap.get("coupon_fee");
        String coupon_count = resultMap.get("coupon_count");
        String transaction_id = resultMap.get("transaction_id");
        String out_trade_no = resultMap.get("out_trade_no");
        String attach = resultMap.get("attach");
        String time_end = resultMap.get("time_end");

        if ("SUCCESS".equals(result_code)) {
            try {
                apiOrderService.wxPaySuccess(out_trade_no, transaction_id, time_end);
            } catch (Exception e) {
                e.printStackTrace();
                return responseFail;
            }
        } else {
            return responseFail;
        }

        System.out.println("return to wx=" + responseSuccess);

        return responseSuccess;
	}


    /**
     * 支付预订单，返回可调起微信支付的相关参数
     * @param openId
     * @param orderNo 订单号
     * @param totalAmount 总支付金额
     * @param attach 商品名称
     * @param ipAddress 用户端ip地址
     * @return
     */
	public WXResponse wxpay(String openId, String orderNo, String totalAmount, String attach, String ipAddress) {
		if (openId == null) {
            return null;
        }
        WXResponse wxResponse = new WXResponse();

        if (StringUtils.hasText(openId)) {
            try
            {
//                String ip = getIpAddr(request);

                String stringB = "appid=" + WeixinConfig.getAppId() + "&attach=" + attach
                        + "&body=" + attach + "&mch_id=" +
                        WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                        + WeixinConfig.getNotifyUrl()
                        +"&openid=" + openId + "&out_trade_no=" + orderNo +
                        "&spbill_create_ip=" + ipAddress + "&total_fee=" + totalAmount
                        + "&trade_type=JSAPI&key=" + WeixinConfig.getKey();

                String sign2 = MD5Util.MD5Upper32(stringB);

                String res = "<xml>" +
                        "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                        "<attach>" + attach + "</attach>" +
                        "<body>" + attach + "</body>" +
                        "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                        "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                        "<notify_url>" + WeixinConfig.getNotifyUrl() + "</notify_url>" +
                        "<openid>" + openId + "</openid>" +
                        "<out_trade_no>" + orderNo + "</out_trade_no>" +
                        "<spbill_create_ip>" + ipAddress + "</spbill_create_ip>" +
                        "<total_fee>" + totalAmount + "</total_fee>" +
                        "<trade_type>JSAPI</trade_type>" +
                        "<sign>" + sign2 + "</sign>" +
                        "</xml>";

                System.out.println("res=" + res);


                HttpPost httpPost = new HttpPost(tengxUrl);

                StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
                httpPost.setEntity(stringEntity);
                CloseableHttpResponse closeableResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
                String wxresp = EntityUtils.toString(closeableResponse.getEntity(), "utf-8");

                System.out.println("resp=" + wxresp);

                if(wxresp.contains("prepay_id"))
                {
                    String t = wxresp.substring(wxresp.indexOf("<prepay_id>") + 11,
                            wxresp.indexOf("</prepay_id>"));

                    String prepay_id = null;
                    if (t.contains("CDATA"))
                    {
                        prepay_id = t.substring(9, t.length() - 3);
                        System.out.println("prepay_id= " + prepay_id);
                        wxResponse.setPrepayId(prepay_id);


                        String timestamp = System.currentTimeMillis()/1000 + "";
                        wxResponse.setTimeStamp(timestamp);

                        //paySign =
                        // MD5(appId=wxd678efh567hg6787
                        // &nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS
                        // &package=prepay_id=wx2017033010242291fcfe0db70013231072
                        // &signType=MD5
                        // &timeStamp=1490840662
                        // &key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6

                        String _package = "prepay_id=" + prepay_id;
                        wxResponse.set_package(_package);

                        String s = "appId=" + WeixinConfig.getAppId() + "&nonceStr=Wm3WZYTPz0wzccnW&package="
                                + _package + "&signType=MD5&timeStamp=" + timestamp + "&key=" + WeixinConfig.getKey();

                        System.out.println("paySignStr = " + s);
                        String sign = MD5Util.MD5Upper32(s);
                        System.out.println("paySign=" + sign);
                        wxResponse.setPaySign(sign);
                        wxResponse.setNonceStr("Wm3WZYTPz0wzccnW");
                        wxResponse.setSignType("MD5");

                        return wxResponse;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
                throw new ServiceException(WX_PAY_ERROR);
            }

        }

        return null;
	}
//
//    public static void main(String[] args) {
//        WXPayService wxPayService = new WXPayService();
//        wxPayService.qrCodePay("aa", "1", "aa", "116.226.157.116");
//    }

    /**
     * 二维码支付，生成支付预订单
     * @param orderNo 订单号
     * @param totalAmount 总支付金额
     * @param attach 商品名称
     * @param ipAddress 用户端ip地址
     * @return  返回二维码支付链接:WXResponse.getCodeUrl()中
     */
	public WXResponse qrCodePay(String orderNo, String totalAmount, String attach, String ipAddress) {
        WXResponse wxResponse = new WXResponse();

            try
            {
//                String ip = getIpAddr(request);

                String stringB = "appid=" + WeixinConfig.getAppId() + "&attach=" + attach
                        + "&body=" + attach + "&mch_id=" +
                        WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                        + WeixinConfig.getNotifyUrl()
                        + "&out_trade_no=" + orderNo +
                        "&spbill_create_ip=" + ipAddress +
                        "&total_fee=" + totalAmount
                        + "&trade_type=NATIVE&key=" + WeixinConfig.getKey();

                String sign2 = MD5Util.MD5Upper32(stringB);


                String res = "<xml>" +
                        "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                        "<attach>" + attach + "</attach>" +
                        "<body>" + attach + "</body>" +
                        "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                        "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                        "<notify_url>" + WeixinConfig.getNotifyUrl() + "</notify_url>" +
                        "<out_trade_no>" + orderNo + "</out_trade_no>" +
                        "<spbill_create_ip>" + ipAddress + "</spbill_create_ip>" +
                        "<total_fee>" + totalAmount + "</total_fee>" +
                        "<trade_type>NATIVE</trade_type>" +
                        "<sign>" + sign2 + "</sign>" +
                        "</xml>";


                System.out.println("res=" + res);


                HttpPost httpPost = new HttpPost(tengxUrl);

                StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
                httpPost.setEntity(stringEntity);
                CloseableHttpResponse closeableResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
                String wxresp = EntityUtils.toString(closeableResponse.getEntity(), "utf-8");

                System.out.println("resp=" + wxresp);

                if(wxresp.contains("prepay_id"))
                {
                    String t = wxresp.substring(wxresp.indexOf("<prepay_id>") + 11,
                            wxresp.indexOf("</prepay_id>"));

                    String prepay_id = null;
                    if (t.contains("CDATA"))
                    {
                        prepay_id = t.substring(9, t.length() - 3);
                        System.out.println("prepay_id= " + prepay_id);
                        wxResponse.setPrepayId(prepay_id);

                        String temp = wxresp.substring(wxresp.indexOf("<code_url>") + 11,
                                wxresp.indexOf("</code_url>"));

                        String code_url = temp.substring(8, t.length() - 5);
                        wxResponse.setCodeUrl(code_url);

                        return wxResponse;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
                throw new ServiceException(WX_PAY_ERROR);
            }


        return null;
	}


    /**
     * 退款
     * @param orderNo 订单号
     * @param totalFee 退款金额
     * @return
     */
	public Boolean doRefund(String orderNo, String totalFee, String refundFee) {
		HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");

        String refundNo = "T" + System.currentTimeMillis();

        String stringB = "appid=" + WeixinConfig.getAppId() + "&mch_id=" +
                WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                + WeixinConfig.reFundNotifyUrl + "&out_refund_no="
                + refundNo
                + "&out_trade_no=" + orderNo +
                "&refund_fee=" + refundFee + "&total_fee=" + totalFee
                + "&key=" + WeixinConfig.getKey();

        System.out.println("string = " + stringB);

        String sign2 = MD5Util.MD5Upper32(stringB);
        System.out.println("sign2 = " + sign2);

        String s = "<xml>" +
                "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                "<notify_url>" + WeixinConfig.reFundNotifyUrl + "</notify_url>" +
                "<out_refund_no>" + refundNo + "</out_refund_no>" +
                "<out_trade_no>" + orderNo + "</out_trade_no>" +
                "<refund_fee>" + refundFee + "</refund_fee>" +
                "<total_fee>" + totalFee + "</total_fee>" +
                "<sign>" + sign2 + "</sign>" +
                "</xml>";

        StringEntity stringEntity = new StringEntity(s, ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        String responseString = null;
        try {
            CloseableHttpResponse closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
            responseString = EntityUtils.toString(closeableHttpResponse.getEntity());

            System.out.println(responseString);

            Map<String, String> resultMap = parserXml(responseString);
             logger.info("refund result  resultMap: " + resultMap);
            if ("SUCCESS".equals(resultMap.get("return_code"))) {
                return true;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
	}


    /**
     * 支付退款查询
     *
     *  微信返回参数见文档：https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_5
     * @param orderNo
     */
    public void getRefund(String orderNo) {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/pay/refundquery");

        String stringB = "appid=" + WeixinConfig.getAppId() + "&mch_id=" +
                WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA"
                + "&out_trade_no=" + orderNo
                + "&key=" + WeixinConfig.getKey();

        System.out.println("string = " + stringB);

        String sign2 = MD5Util.MD5Upper32(stringB);
        System.out.println("sign2 = " + sign2);

        String s = "<xml>" +
                "<appid>" + WeixinConfig.getAppId() + "</appid>" +
                "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                "<out_trade_no>" + orderNo + "</out_trade_no>" +
                "<sign>" + sign2 + "</sign>" +
                "</xml>";
        StringEntity stringEntity = new StringEntity(s, ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        String responseString = null;
        try {
            CloseableHttpResponse closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
            responseString = EntityUtils.toString(closeableHttpResponse.getEntity());

            System.out.println(responseString);

            Map<String, String> resultMap = parserXml(responseString);

            if ("SUCCESS".equals(resultMap.get("return_code"))) {

                String result_code = resultMap.get("result_code");
                if ("SUCCESS".equals(result_code)) {//退款申请接收成功

                } else if ("FAIL".equals(result_code)) {

                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 退款通知
     * @param request
     * @return
     */
    public String reFundNotify(HttpServletRequest request) {
        System.out.println("================   weixin reFundNotify ================");
        logger.info("weixin reFundNotify");
        StringBuilder sb = new StringBuilder();
        try
        {
            BufferedReader ina = new BufferedReader(
                    new InputStreamReader(request.getInputStream()));
            String line = null;
            while ((line = ina.readLine()) != null)
            {
                sb.append(line);
            }
            logger.info("body: " + sb);
            System.out.println("body : " + sb);
        }catch (Exception e){
            e.printStackTrace();
        }

        Map<String, String> resultMap = parserXml(sb.toString());
        logger.info("refund resultMap: " + resultMap);
        System.out.println("========  refund resultMap: =========== " + resultMap);
        String appid = resultMap.get("appid");
        String mch_id = resultMap.get("mch_id");
        String nonce_str = resultMap.get("nonce_str");
        String return_code = resultMap.get("return_code");

        if ("SUCCESS".equals(return_code)) {
            try {
                String req_info = resultMap.get("req_info");
                String decodedData = decryptData(Base64.getDecoder().decode(req_info));

                Map<String, String> decodedMap = parserXml(decodedData);
                logger.info("refund decodedMap: " + decodedMap);
                System.out.println("orefund decodedMap: " + decodedMap);

                String refund_status = decodedMap.get("refund_status");
                String out_trade_no = decodedMap.get("out_trade_no");
                String transaction_id = decodedMap.get("transaction_id");
                //SUCCESS-退款成功
                //CHANGE-退款异常
                //REFUNDCLOSE—退款关闭
                if ("SUCCESS".equals(refund_status)) {
                    apiOrderService.updateRefundOrder(out_trade_no, RefundStatus.REFUDN_OK);
                } else if ("CHANGE".equals(refund_status)) {
                    apiOrderService.updateRefundOrder(out_trade_no, RefundStatus.REFUND_FAIL);
                } else if ("REFUNDCLOSE".equals(refund_status)) {
                    apiOrderService.updateRefundOrder(out_trade_no, RefundStatus.REFUND_FAIL);
                }

            } catch (Exception e) {
                e.printStackTrace();
                return responseFail;
            }
        } else {
            return responseFail;
        }
        logger.info("return to wx= " + responseSuccess);
        System.out.println("======= return to wx ================" + responseSuccess);

        return responseSuccess;
    }

    //------------private method ------------

    private static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private static Map<String, String> parserXml(String content) {
        SAXReader saxReader = new SAXReader();
        Map<String, String> resultMap = new HashMap<>();
        try {
            StringReader stringReader = new StringReader(content);
            Document document = saxReader.read(stringReader);
            Element employees = document.getRootElement();


            for (Iterator i = employees.elementIterator(); i.hasNext();) {
                Element employee = (Element) i.next();
                System.out.println(employee.getName() + ":" + employee.getText());

                resultMap.put(employee.getName(), employee.getText());
            }
        } catch (DocumentException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("dom4j parserXml");

        return resultMap;
    }

    private static String getPostSign(Map map)
    {
        // 对参数名进行字典排序
        Set strings = map.keySet();
        String[] keyArray = (String[])strings.toArray(new String[strings.size()]);

        Arrays.sort(keyArray, String.CASE_INSENSITIVE_ORDER);

        // 拼接有序的参数名-值串
        StringBuilder stringBuilder = new StringBuilder();
        for (String key : keyArray)
        {
            if ("sign".equals(key))
            {
                continue;
            }
            stringBuilder.append(key).append("=").append(map.get(key)).append("&");
        }
        return stringBuilder.toString();
    }

    /**
     * 密钥算法
     */
    private static final String ALGORITHM = "AES";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
    /**
     * 生成key
     */
    private static SecretKeySpec key = new SecretKeySpec(MD5.stringToMD5(WeixinConfig.getKey()).toLowerCase().getBytes(), ALGORITHM);

    public static String decryptData(byte[] decodeStr) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(decodeStr));
    }


    /**
     * 用户被扫刷卡支付
     * @param orderNo 订单号
     * @param total_fee 总金额 单位为分
     * @param ip 终端ip
     * @param auth_code 用户支付二维码
     *
     * @return 支付结果：true成功  false失败
     */
    public boolean micropay(String orderNo, String total_fee, String ip, String auth_code){
        Map<String, String> data = new HashMap<>();
        data.put("body", "金华第二中学-食堂");
        data.put("out_trade_no", orderNo);
        data.put("total_fee", total_fee);
        data.put("spbill_create_ip", ip);
        data.put("auth_code", auth_code);
        try {
            Map<String, String> result = wxPay.microPay(data);
            if (result != null) {

                String return_code = result.get("return_code");

                if (success.equals(return_code)) {
                    String result_code = result.get("result_code");
                    if (success.equals(result_code)) {
                        return true;
                    } else {
                        String err_code = result.get("err_code");
                        if ("USERPAYING".equals(err_code)) {
                            //用户输入密码中，每隔5秒查询状态

                            int count = 0;
                            boolean doWhile = false;

                            Map<String, String> orderQueryData = new HashMap<>();
                            orderQueryData.put("out_trade_no", orderNo);

                            while(!doWhile) {
                                try {
                                    Thread.sleep(5 * 1000); //设置暂停的时间 5 秒
                                    count ++ ;
                                    System.out.println(sdf.format(new Date()) + "--订单查询支付状态请求第" + count + "次");


                                    Map<String, String> orderQueryResult = wxPay.orderQuery(orderQueryData);

                                    String order_return_code = orderQueryResult.get("return_code");

                                    if (success.equals(order_return_code)) {
                                        String order_result_code = orderQueryResult.get("result_code");
                                        if (success.equals(order_result_code)) {
                                            String trade_state = orderQueryResult.get("trade_state");
                                            if (success.equals(trade_state)) {
                                                return true;
                                            } else if (!"USERPAYING".equals(trade_state)) {
                                                return false;
                                            }
                                        }
                                    }

                                    if (count == 6) {
                                        doWhile = true;

                                        //撤销订单
                                        wxPay.reverse(orderQueryData);
                                        return false ;
                                    }
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }

                        }
                    }

                } else {
                    return false;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }



    public String doPay(HttpServletRequest request, HttpServletResponse response, Model model) {
        boolean inWeixin = false;

        System.out.println(request.getHeader("User-Agent"));
        if(request.getHeader("User-Agent").contains("MicroMessenger")){
            inWeixin = true;
        }
        String orderNo = request.getParameter("orderNo");
        String totalFee = request.getParameter("totalFee");
        String ip = getIpAddr(request);

        System.out.println("orderNo=" + orderNo);

        if (inWeixin) {
            String requestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
                    + WeixinConfig.appId + "&redirect_uri=http://binguoai.com/api/wxpay/callback" +
                    "?orderNo=" + orderNo + URLEncoder.encode(
                    "&totalFee=") + totalFee +
                    "&response_type=code&scope=snsapi_base&state=1#wechat_redirect";
            try
            {
                System.out.println("redirecturl = " + requestUrl);
                response.sendRedirect(requestUrl);
                return null;
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }else {
            try
            {
                String stringB = "appid=" + WeixinConfig.appId + "&attach=" + "菜品"
                        + "&body=" + "菜品" + "&mch_id=" +
                        WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                        + WeixinConfig.getNotifyUrl()
                        + "&out_trade_no=" + orderNo +
                        "&spbill_create_ip=" + ip + "&total_fee=" + totalFee
                        + "&trade_type=MWEB&key=" + WeixinConfig.getKey();

                System.out.println("string = " + stringB);

                String sign2 = MD5Util.MD5Upper32(stringB);
                System.out.println("sign2 = " + sign2);

                String res = "<xml>" +
                        "<appid>" + WeixinConfig.appId + "</appid>" +
                        "<attach>" + "菜品" + "</attach>" +
                        "<body>" + "菜品" + "</body>" +
                        "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                        "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                        "<notify_url>" + WeixinConfig.getNotifyUrl() + "</notify_url>" +
                        "<out_trade_no>" + orderNo + "</out_trade_no>" +
                        "<spbill_create_ip>" + ip + "</spbill_create_ip>" +
                        "<total_fee>" + totalFee + "</total_fee>" +
                        "<trade_type>MWEB</trade_type>" +
                        "<sign>" + sign2 + "</sign>" +
                        "</xml>";

                System.out.println("res=" + res);


                HttpPost httpPost = new HttpPost(tengxUrl);

                StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
                httpPost.setEntity(stringEntity);
                CloseableHttpResponse closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
                String resp = EntityUtils.toString(closeableHttpResponse.getEntity());

                System.out.println("resp=" + resp);

                if(resp.contains("mweb_url"))
                {
                    String t = resp.substring(resp.indexOf("<mweb_url>") + 11,
                            resp.indexOf("</mweb_url>"));

                    if (t.contains("CDATA"))
                    {
                        String mweb_url = t.substring(8, t.length() - 3);

                        System.out.println("murl=" + mweb_url);
                        model.addAttribute("mweb_url", mweb_url);

                        return "inwx";
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        return "fail";
    }


    public String wxpay(Model model, HttpServletRequest request) {
        String code = request.getParameter("code");
        String orderNo = request.getParameter("orderNo");
        String totalFee = request.getParameter("totalFee");

        if (StringUtils.hasText(code)) {
            String getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                    "appid=" + WeixinConfig.appId + "&secret=" + WeixinConfig.getSecret() + "&" +
                    "code=" + code + "&grant_type=authorization_code";

            try
            {
                HttpGet httpGet = new HttpGet(getAccessTokenUrl);

                CloseableHttpResponse closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpGet);
                String resp = EntityUtils.toString(closeableHttpResponse.getEntity());

                System.out.println("tokenResponce=" + resp);
                WXOpenID wxOpenID = JsonUtil.parse(resp, WXOpenID.class);
                String openId = wxOpenID.getOpenid();

                String queryString = request.getQueryString();
                String url = payUrl;
                if(StringUtils.hasText(queryString)){
                    url += "?" + queryString;
                }

//                url = url.substring(0, url.indexOf("&code="));

                System.out.println("url=" + url);

                try
                {
                    String ip = getIpAddr(request);
//                    String ip2 = request.getRemoteAddr();

                    String stringB = "appid=" + WeixinConfig.appId + "&attach=" + "菜品"
                            + "&body=" + "菜品" + "&mch_id=" +
                            WeixinConfig.getMerId() + "&nonce_str=ibuaiVcKdpRxkhJA&notify_url="
                            + WeixinConfig.getNotifyUrl()
                            +"&openid=" + openId + "&out_trade_no=" + orderNo +
                            "&spbill_create_ip=" + ip + "&total_fee=" + totalFee
                            + "&trade_type=JSAPI&key=" + WeixinConfig.getKey();

                    String sign2 = MD5Util.MD5Upper32(stringB);

                    String res = "<xml>" +
                            "<appid>" + WeixinConfig.appId + "</appid>" +
                            "<attach>" + "菜品" + "</attach>" +
                            "<body>" + "菜品" + "</body>" +
                            "<mch_id>" + WeixinConfig.getMerId() + "</mch_id>" +
                            "<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>" +
                            "<notify_url>" + WeixinConfig.getNotifyUrl() + "</notify_url>" +
                            "<openid>" + openId + "</openid>" +
                            "<out_trade_no>" + orderNo + "</out_trade_no>" +
                            "<spbill_create_ip>" + ip + "</spbill_create_ip>" +
                            "<total_fee>" + totalFee + "</total_fee>" +
                            "<trade_type>JSAPI</trade_type>" +
                            "<sign>" + sign2 + "</sign>" +
                            "</xml>";

                    System.out.println("res=" + res);


                    HttpPost httpPost = new HttpPost(tengxUrl);

                    StringEntity stringEntity = new StringEntity(res, ContentType.APPLICATION_JSON);
                    httpPost.setEntity(stringEntity);
                    CloseableHttpResponse closeableResponse = HttpConnectionManager.getHttpClient().execute(httpPost);
                    String wxresp = EntityUtils.toString(closeableResponse.getEntity());

                    System.out.println("resp=" + wxresp);

                    if(wxresp.contains("prepay_id"))
                    {
                        String t = wxresp.substring(wxresp.indexOf("<prepay_id>") + 11,
                                wxresp.indexOf("</prepay_id>"));

                        String prepay_id = null;
                        if (t.contains("CDATA"))
                        {
                            prepay_id = t.substring(9, t.length() - 3);
                            System.out.println("prepay_id= " + prepay_id);
                            model.addAttribute("prepay_id", prepay_id);


                            String timestamp = System.currentTimeMillis()/1000 + "";
                            model.addAttribute("timestamp", timestamp);

                            try {
                                String jsapi_ticket = WeixinConfig.getJsapiTicket();
                                if(StringUtils.isEmpty(jsapi_ticket)) {
                                    refreshWxToken();

                                    jsapi_ticket = WeixinConfig.getJsapiTicket();
                                }

                                String s = "jsapi_ticket=" + jsapi_ticket + "&noncestr=Wm3WZYTPz0wzccnW&timestamp=" + timestamp + "&url=" + url;

                                System.out.println("url = " + url);
                                System.out.println("jsapi_ticket = " + jsapi_ticket);
                                System.out.println("s = " + s);
                                String sign = DigestUtils.sha1Hex(s);
                                System.out.println("signature=" + sign);
                                model.addAttribute("signature", sign);

                            }catch (Exception e ){
                                e.printStackTrace();
                            }

                            String _package = "prepay_id=" + prepay_id;

                            String s = "appId=" + WeixinConfig.appId + "&nonceStr=Wm3WZYTPz0wzccnW&package="
                                    + _package + "&signType=MD5&timeStamp=" + timestamp + "&key=" + WeixinConfig.getKey();

                            System.out.println("paySignStr = " + s);
                            String sign = MD5Util.MD5Upper32(s);
                            System.out.println("paySign=" + sign);
                            model.addAttribute("paySign", sign);

                            return "wx";
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }


            }catch (Exception e){
                e.printStackTrace();
                return "fail";
            }
        }

        return "fail";
    }

    @PostConstruct
    public void refreshWxToken() throws Exception{
        try {
            String expires_in = handler();

            if (expires_in == null) {
                return;
            }

            ThreadManager.scheduledThreadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, Integer.valueOf(expires_in), Integer.valueOf(expires_in), TimeUnit.SECONDS);
        } catch (Exception e) {

        }
    }

    public String handler() throws Exception{
        HttpGet httpPost = new HttpGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
                + WeixinConfig.appId + "&secret=" + WeixinConfig.getSecret());

        CloseableHttpResponse closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpPost);

        String tokenResponce = EntityUtils.toString(closeableHttpResponse.getEntity());
        try{

            System.out.println("getToken response:" + tokenResponce);
            WXToken wxToken = JsonUtil.parse(tokenResponce, WXToken.class);
            String token = wxToken.getAccess_token();
            WeixinConfig.setAccessToken(token);

            HttpGet httpGet = new HttpGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token + "&type=jsapi");

            closeableHttpResponse = HttpConnectionManager.getHttpClient().execute(httpGet);
            String ticketResponce = EntityUtils.toString(closeableHttpResponse.getEntity());


            WXTicket wxTicket = JsonUtil.parse(ticketResponce, WXTicket.class);
            WeixinConfig.setJsapiTicket(wxTicket.getTicket());

            return wxToken.getExpires_in();
        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }


    private String getRequestBody(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try
        {
            BufferedReader ina = new BufferedReader(
                    new InputStreamReader(request.getInputStream()));
            String line = null;
            while ((line = ina.readLine()) != null)
            {
                sb.append(line);
            }
            logger.info("weixin pay body:  " + sb  );

        }catch (Exception e){
            e.printStackTrace();
        }

        return sb.toString();
    }
}
